# First example of PyQt + PyOpenGL programming, with VBO and shaders.
# Robert Bridson
# Released under the GPLv3

# Get the Python Qt bindings (PyQt4 or PySide)
from PyQt4.QtCore import *
from PyQt4.QtGui import *
# Also get the bridge from Qt to OpenGL
from PyQt4.QtOpenGL import *
# OpenGL itself of course
import OpenGL
from OpenGL.GL import *
# Need numpy for data arrays
from numpy import array
# And basic operating system stuff (to quit)
import sys

# Basic GL widget to play with (inherit from QGLWidget, override a few methods)
class MyGL(QGLWidget):
    def __init__(self, parent):
        QGLWidget.__init__(self, parent)
        self.setMinimumSize(QSize(400,400))

    def initializeGL(self):
        # This method gets called once, after OpenGL has started up.

        # Basic OpenGL initialization.
        glClearColor(0.0, 0.0, 0.0, 0.0)
        glClearDepth(1)

        # Set up shaders. We actually embed the source here, and compile/link
        # on the fly. 

        # first the vertex shader - very basic
        vshader_src = """
            #version 110
            varying vec4 paramCoord;
            void main()
            {
                gl_Position = vec4(gl_Vertex.x, gl_Vertex.y, 0, 1);
                paramCoord = vec4(gl_Vertex.x, gl_Vertex.y, gl_Vertex.z, 1);
            }
            """
        vshader_id = glCreateShader(GL_VERTEX_SHADER)
        glShaderSource(vshader_id, vshader_src)
        glCompileShader(vshader_id)
        print("vertex shader compile status: "+
                str(glGetShaderiv(vshader_id, GL_COMPILE_STATUS)))
        print(glGetShaderInfoLog(vshader_id))

        # then the fragment shader - showing a hint of what's possible
        fshader_src = """
            #version 110
            varying vec4 paramCoord;
            void main()
            {
                float r = paramCoord.x;
                float g = paramCoord.y;
                float b = 0.5+0.5*sin(50.0*(paramCoord.x+paramCoord.y));
                gl_FragColor = vec4(r, g, b, 1);
            }
            """
        fshader_id = glCreateShader(GL_FRAGMENT_SHADER)
        glShaderSource(fshader_id, fshader_src)
        glCompileShader(fshader_id)
        print("fragment shader compile status: "+
                str(glGetShaderiv(fshader_id, GL_COMPILE_STATUS)))
        print(glGetShaderInfoLog(fshader_id))

        # then link them together into the shader program for use later
        self.shader = glCreateProgram()
        glAttachShader(self.shader, vshader_id)
        glAttachShader(self.shader, fshader_id)
        glLinkProgram(self.shader)
        print("shader program link status: "+
                str(glGetProgramiv(self.shader, GL_LINK_STATUS)))

        # set up vertex and element buffer objects for very simple (1 tri) mesh
        self.meshbuffers = glGenBuffers(2)
        glBindBuffer(GL_ARRAY_BUFFER, self.meshbuffers[0])
        self.vertex = array([-1, -1, 0, 1,
                              1, -1, 0, 1,
                             -1,  1, 0, 1], dtype='float32')
        glBufferData(GL_ARRAY_BUFFER, self.vertex, GL_STATIC_DRAW)
        glVertexPointer(4, GL_FLOAT, 0, None)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.meshbuffers[1])
        self.triangle = array([0, 1, 2], dtype='uint32')
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.triangle, GL_STATIC_DRAW)

    def resizeGL(self, w, h):
        # this gets called whenever the widget is resized.
        glViewport(0, 0, w, h) 
        # in a more advanced program, this would also update the projection
        # matrix to account for any change in aspect ratio.

    def paintGL(self):
        # this gets called when the widget needs to redraw - this is the
        # OpenGL rendering code. As you can see, in more modern OpenGL, very
        # few function calls are needed to potentially render very complicated
        # stuff.
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glUseProgram(self.shader)
        glEnableClientState(GL_VERTEX_ARRAY)
        glBindBuffer(GL_ARRAY_BUFFER, self.meshbuffers[0])
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.meshbuffers[1])
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, None)

# Initialize main GUI, returning the base widget
# (Could use MainWindow, or many other things - there's nothing interesting
# here or furher below)
def initGUI():
    window = QWidget()
    window.setWindowTitle("firstQT")
    
    layout = QVBoxLayout()
    
    window.setLayout(layout)

    glthing = MyGL(window)
    layout.addWidget(glthing, 1)

    label = QLabel("hi there")
    layout.addWidget(label, 0)

    return window

# Execute
if __name__ == "__main__":
    # Create the (single) Qt Application object, which starts everything up
    app = QApplication(sys.argv)
    # Get our main window
    gui = initGUI()
    gui.show()
    gui.setFocus()
    # Run it
    app.exec_()
    sys.exit()
